7W - EKS Fargate
개요
이 문서에서는 컨테이너 환경을 서버리스로 띄우는 Fargate에 대해서 알아본다.
간단한 동작 방식과 실습을 진행할 것이다.
사전 지식
파게이트란
파게이트는 서버리스 리소스의 일종으로, EKS와 같은 컨테이너 환경을 위해 제공되는 관리형 서비스이다.
컨테이너를 띄우긴 띄우는데, 서버리스로 띄우는 것이 파게이트라는 서비스다!
클러스터에서 서버리스를 쓰는 장점은 그냥 서버리스를 쓸 때의 장점과 같다.
극한의 효율로 필요한 만큼의 자원만 사용하고, 사용한 만큼만 돈을 내는 것.
(물론 서버리스 자체가 동일 스펙 대비 비용이 높기에 그렇게 좋은지는 모르겠다;)
파게이트에 올라가는 파드는 그 자체가 하나의 노드가 된다.
그래서 별도의 클러스터 오토스케일링이 필요없다는 것이 큰 장점이다.
(카펜터 믿고 쓰면 되지 않을까 싶긴 한데..)
사용 케이스
이미 쿠버네티스 클러스터를 구축한다는 것 자체가 사실 자원을 효율적으로 사용하기 위한 것이다.
근데 여기에 왜 굳이 서버리스 환경을 들여서 또 비용 최적화를 할까?
파게이트를 써서 비용최적화를 할 수 있는 케이스가 뭐가 있을까?
흔한 사용 케이스 중 하나로, 카펜터를 파게이트에 올리는 경우가 있다.
카펜터를 사용하게 되면 카펜터가 올라간 노드는 카펜터에 의해 관리를 받으면 안 된다.
그래서 실제 운영 환경 자체는 일반 인스턴스를 쓰되, 카펜터를 인스턴스 위에 배치하지 않고 파게이트에 올림으로써 효율적으로 인스턴스 환경을 관리하는 것이다.
그리고 카펜터가 클러스터 효율화를 못 시키는 시간대가 고정돼있다면 그 때는 아예 카펜터조차 꺼버리는 식으로 비용 효율화를 할 수 있는 것이다.
두번째 사용 케이스는 일시적으로만 실행되는 워크로드를 올리는 것이다.
대표적으로 잡 같은 것은 애초에 지속적인 프로세스가 아니므로, 순간적으로만 자원이 필요하다.
이런 것들은 일찌감치 파게이트를 활용해 정확하게 필요한 만큼만 비용을 지불하는 것이 효과적이다.
흔히 배치 파이프라인 작업, CICD 등의 작업을 할 때 파게이트를 사용하는 것은 꽤나 효과적이다.
서버리스 기술 원리
AWS의 서버리스는 Firecracker라는 툴을 기반으로 한다.[1]
파이어크래커는 KVM을 이용해 간단하게 가상 머신을 만드는 툴인데, 이로부터 호스트와의 격리를 완벽하게 해내면서도 성능을 최대한 끌어올려 아주 작은 경량의 vm을 여러 개 만들 수 있도록 한다.
파이어크래커에서 가장 신경쓰는 것이 두 가지 있다면 바로 보안과 성능이다.
이때 보안이 매우 강력한 특징 중 하나인데, 이 그림에서 보다시피 실제 vm은 이중 방벽으로 막혀있다.
일단 가상화로 인한 방벽이 하나 세워지고(이것만으로 충분한 거 아니었어..?), 여기에 seccomp, 네임스페이스 등의 리눅스의 보안 기능을 할 수 있는 모듈들로 Jailer 방벽이 세워진다.
동작 방식
스케줄링 과정
파게이트로서 서버리스 환경에 파드가 배치되는 것은 스케줄링 단계에서 정해진다.
특정 조건을 만족하는 파드가 배포된다면 Admission Webhook이 동작하여 해당 파드의 스케줄러를 fargate-scheduler로 변경해버린다.
그러면 컨트롤 플레인 어딘가의 스케줄러가 동작하며 파드를 어디에 배치할지 고른다.
이 과정에서 위에 보이는 vm 하나가 생겨나게 되며, 이 vm에 단 하나의 파드가 배치되도록 만든다.
인스턴스 스펙
이때 배치되는 파드의 자원 요청 값에 따라 인스턴스의 스펙이 정해지는데, 아무런 요청도 하지 않고 만들면 최소 값으로 지정된다.
필요한 만큼의 양을 적절하게 요청하는 것이 중요하겠다.
요청량의 값을 위의 표에 맞춰 적절히 올림하여 인스턴스가 만들어진다.
이때 내부에 따로 동작하는 데몬 프로세스 등으로 인해 실질적으로 메모리는 250메가 정도가 추가로 합산된다.[2]
즉, 만약 cpu 0.1에 메모리 0.4기가를 요청한다고 해도, 실제로는 메모리는 0.65기가로 계산되어 결과적으로 cpu 0.25, 메모리 1기가의 인스턴스가 배포될 것이다.
다만 직접적으로 노드를 보면 이 값과 다르게 출력이 될 텐데, 실제 비용에 계산되는 스펙 정보를 보려면 그 인스턴스에 배치된 파드의 어노테이션 값을 보면 된다.
스토리지로는 기본 20기가의 로컬 스토리지가 부여된다.
이것도 ephemral-storage
를 통해 최대 175기가까지 높여서 받을 수 있는데, 많이 필요하다 하면 어차피 내부에 Amazon EFS 파일시스템을 부착해주니 이걸 활용하자.
인스턴스 환경
일단 파게이트가 배치되는 서브넷에는 파게이트의 ENI가 부착된다.
보다시피 요청자는 사용자가 아니며, 인스턴스 소유자 역시 마찬가지이다.
이를 통해 파게이트는 클러스터가 배포된 VPC에서 접근이 가능하게 된다.
클러스터적으로 통신이 가능한 이유는 인스턴스에 기본적으로 VPC CNI 프로세스가 동작하기 때문이다.
이 CNI는 파드에 인스턴스의 IP를 그대로 부여해주며, 그래서 실제로 뜯어보면 노드 ip와 파드 ip가 같은 것을 확인할 수 있다.
이를 통해 클러스터에 네트워크 활동을 할 수 있게 되는 것이다!
여기에 추가적으로 Fluentbit 프로세스가 동작하여, 인스턴스, 어플리케이션 로깅이 가능하게 된다.
참고로 이것 때문에 관련한 세팅을 조금 해줘야 하는데, 이건 [[#EKS 설정]]에서 본다.
주의 사항
파게이트에는 여러 가지 특징과 제한 사항이 많다.
그래서 사용할 수 있는 워크로드가 한정되니 잘 참고하자.
- 프라이빗 서브넷에만 배치가 가능하다.
- 사실 이건 어쩌면 당연한 게, 파드 하나당 인스턴스가 생기는데 이게 전부 다 공인 ip를 받는다하면..
- 아무튼 이것 때문에 nat 게이트웨이를 안 뚫어두면 이미지를 못 받아오는 불상사도 생길 수 있으니 주의하자.
- ALB Controller에서 IP 모드만 사용 가능하다.
- 데몬셋은 실행되지 않는다.
- 비슷한 맥락으로, hostNetwork나 hostPath 등 노드와 밀접하게 연관되는 모든 방식의 설정이 불가능하다.
- GPU환경도 세팅할 수 없다.
- IMDS가 불가능하다.
- Amazon EFS가 드라이버 설치 없이 기본적으로 활성화돼있다.
- 다만 실제로 사용하려면 당연히 일단 본인 efs를 만들어야 하며, 오직 정적 프로비저닝만이 가능하다.
- 파드가 성공이나 실패를 한다고 인스턴스가 삭제되지 않는다.
- 이게 매우 중요한 게, Job 같은 경우 설정 없이 그냥 파게이트를 쓰면 잡 실행이 완료돼도 인스턴스가 계속 남아 돈이 나가는 수가 있다.
실습 진행
테라폼 세팅
module "fargate_profile" {
source = "terraform-aws-modules/eks/aws//modules/fargate-profile"
version = "20.34.0"
name = "fargate"
cluster_name = module.eks.cluster_name
subnet_ids = module.eks_vpc.private_subnets_id
# ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
selectors = [
{
namespace = "fargate"
},
{
namespace = "kube-system"
}
]
tags = {
Environment = "dev"
Terraform = "true"
}
}
꽤나 세팅은 간단하다.
파게이트를 사용할 프로필을 지정하는데, 이 프로필은 네임스페이스를 걸거나 라벨 셀렉터를 걸어준다.
그럼 지정한 라벨이나 혹은 네임스페이스를 달아서 파드를 만들 때, 해당 파드의 정보에 따라 뮤테이팅이 발동하여 파게이트 스케줄러가 동작하게 될 것이다.
로깅 설정
여기에 추가적으로 세팅할 것이 있다.
단순하게 파드를 배치하려고 하면 로깅 관련한 에러를 마주하게 되는데, 위와 같은 컨피그맵이 필요하다.[3]
apiVersion: v1
kind: Namespace
metadata:
labels:
aws-observability: enabled
name: aws-observability
spec: {}
---
kind: ConfigMap
apiVersion: v1
metadata:
name: aws-logging
namespace: aws-observability
data:
flb_log_cw: "false" # Set to true to ship Fluent Bit process logs to CloudWatch.
filters.conf: |
[FILTER]
Name parser
Match *
Key_name log
Parser crio
[FILTER]
Name kubernetes
Match kube.*
Merge_Log On
Keep_Log Off
Buffer_Size 0
Kube_Meta_Cache_TTL 300s
output.conf: |
[OUTPUT]
Name cloudwatch_logs
Match kube.*
region region-code
log_group_name my-logs
log_stream_prefix from-fluent-bit-
log_retention_days 60
auto_create_group true
parsers.conf: |
[PARSER]
Name crio
Format Regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>P|F) (?<log>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
이건 컨피그맵 데이터 예시이다.
여기에서 로깅 관련한 설정들을 넣어주면 되며, 이를 통해 파게이트 파드에 대한 로깅이 가능해진다.
coredns 설정
Core DNS를 파게이트 위에 실행하려고 하면 잘 안 될 것이다.
기본 eks에서는 core dns가 일반 인스턴스에 올라가길 기대하기 때문인데, 이에 관련한 조금의 수정이 필요하다.
kubectl patch deployment coredns -n kube-system --type json -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'
어노테이션 중에 compute type에 대한 정보를 지우면 파게이트에 다시 들어가게 될 것이다.
이렇게 들어가있어도 실행이 안 될 때가 있으니 디플로이먼트를 재시작을 해보는 게 좋다.
기본 확인
eks 콘솔에서는 compute 탭에 들어가면 프로필들을 확인할 수 있다.
기본적으로 세팅될 때 파게이트는 두 가지 정책을 부여 받는다.
먼저 기본적으로 클러스터 네트워크를 위한 CNI 정책이 하나 붙는다.
파게이트가 띄워진 인스턴스에 CNI 프로세스가 띄워져있을 것이라 추측해볼 수 있다.
또한 ECR에 접근하여 이미지를 받아오는 정책도 받는데, 말 그대로 ECR에서 이미지를 가져올 때 사용될 것으로 보인다.
근데 이름이 PodExcecution인 것을 보면, 파게이트의 컨테이너 환경을 구축하기 위한 다른 소프트웨어를 받기 위함일 수도 있다.
노드 확인
k get nodes
파게이트에서 파드가 하나의 노드에 배치되기 때문에, 노드가 매우 많아지는 것을 볼 수 있다.
정직하게 하나의 파드만 배치된다..
k get nodes fargate-ip-192-168-12-142.ap-northeast-2.compute.internal -oyaml
아무 노드나 뜯어서 살펴보면 위와 같이 테인트가 걸리는 것을 확인할 수 있다.
한 파게이트에 두 파드 띄우기를 실험해보고 싶었는데, 애초에 파드 개수에 제한이 걸려 있어 그건 불가능하다.
아무런 자원 요청 정보 없이 띄웠을 때 생기는 노드는 위와 같이 cpu 2개, 메모리 4기가로 세팅된다.
노드의 정보는 ec2 인스턴스가 아니기에 인스턴스로는 확인할 수 없지만, 그럼에도 서브넷에 연결되는 네트워크 인터페이스 정보든 확인할 수 있다.
보안그룹은 클러스터의 기본 보안그룹이 세팅된다.
요청자 ID, 인스턴스 소유자가 전부 다르다는 것도 하나의 특징이다.
파드 확인
각 파드는 해당 노드의 IP와 정확하게 일치하는 IP를 가지게 된다.
파드의 스펙 정보를 보면, 스케줄러가 fargate-scheduler로 바뀐 것을 확인할 수 있다.
또한 중요도가 system-node-critical로 설정된다.
이 값은 가장 높은 값이라 이걸 넘길 수 없고, 어떤 파드도 이 파드를 축출시키며 선점할 수 없게 된다.
k get mutatingwebhookconfigurations.admissionregistration.k8s.io
스케줄러와 각종 설정을 넣는 것은 뮤테이팅 웹훅을 통해 이뤄진다.
애드온 설치
파게이트 환경에서 다양한 애드온들을 설치해보려고 한다.
구체적으로 설치하려는 것은 다음과 같다.
- alb controller
- external dns
- kube ops view
####################################################
##### kube ops view
####################################################
locals {
ops_name_in_cluster = "kube-ops-view"
ops_namespace = "addon-kube-ops-view"
}
resource "helm_release" "kube_ops_view" {
name = "kube-ops-view"
repository = "https://geek-cookbook.github.io/charts"
chart = "kube-ops-view"
version = "1.2.2"
create_namespace = true
namespace = local.ops_namespace
values = [
<<-EOF
env:
TZ: Asia/Seoul
service:
main:
type: ClusterIP
EOF
]
}
resource "kubernetes_ingress_v1" "kube_ops_view" {
metadata {
name = "kube-ops-view"
namespace = local.ops_namespace
labels = {
"app.kubernetes.io/name" = "kube-ops-view"
}
annotations = {
"alb.ingress.kubernetes.io/certificate-arn" = data.aws_acm_certificate.domain.arn
"alb.ingress.kubernetes.io/listen-ports" = "[{\"HTTPS\":443}, {\"HTTP\":80}]"
"alb.ingress.kubernetes.io/load-balancer-name" = "${local.cluster_name}-ingress-alb"
"alb.ingress.kubernetes.io/scheme" = "internet-facing"
"alb.ingress.kubernetes.io/ssl-redirect" = "443"
"alb.ingress.kubernetes.io/success-codes" = "200-399"
"alb.ingress.kubernetes.io/target-type" = "ip"
}
}
spec {
ingress_class_name = "alb"
rule {
host = "kubeopsview.${local.domain_name}"
http {
path {
backend {
service {
name = "kube-ops-view"
port {
number = 8080
}
}
}
path = "/"
path_type = "Prefix"
}
}
}
}
depends_on = [
helm_release.lbc,
helm_release.external_dns
]
}
모든 코드를 여기에 다루지는 않겠다.
왜냐하면 코드가 달라진 것이 없기 때문이다.
딱 하나, 파게이트 프로필에 해당하는 네임스페이스를 지정해주었다.
파게이트 노드 뺏어버리기!
파게이트에는 한 노드에 하나의 파드만 배치된다.
근데, 그 노드를 뺏을 수는 없을까?
안 된다는 결론을 고되게 얻어보자.
apiVersion: v1
kind: Pod
metadata:
name: debug
namespace: default
spec:
containers:
- image: nicolaka/netshoot
name: debug
command:
- sh
- -c
- "tail -f /dev/null"
간단하게 파드를 만드려고 하면, default 네임스페이스를 배치할 노드가 없어서 아무런 일도 발생하지 않는다.
파게이트 프로필에서 default 네임스페이스에 대한 설정은 하지 않았기에 기본 스케줄러가 동작한다.
tolerations:
- key: "eks.amazonaws.com/compute-type"
operator: "Exists"
톨러레이션 이슈로 보이더라도, 어차피 위에서 보았듯 각 노드는 하나의 파드만 실행할 수 있기에 소용 없다.
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: custom-critical
description: 해해햏
preemptionPolicy: PreemptLowerPriority
value: 2000001001
아예 파게이트를 선점하게 하고 싶어도 불가능하다.
labels:
eks.amazonaws.com/fargate-profile: fargate
...
schedulerName: fargate-scheduler
아예 파게이트 스케줄러를 사용하도록 해본다.
결국엔 소용없다.
샘플 앱 배포
k create ns fargate
파게이트 네임스페이스를 사용하기로 했으니 네임스페이스를 만든다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test
namespace: fargate
annotations :
"alb.ingress.kubernetes.io/scheme" : "internet-facing"
"alb.ingress.kubernetes.io/success-codes" : "200-399"
"alb.ingress.kubernetes.io/target-type" : "ip"
spec:
ingressClassName: alb
rules:
- host: test.zerotay.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: test
port:
number: 80
---
apiVersion: v1
kind: Service
metadata:
name: test
namespace: fargate
spec:
selector:
app: test
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
namespace: fargate
labels:
app: test
spec:
selector:
matchLabels:
app: test
replicas: 2
template:
metadata:
labels:
app: test
spec:
containers:
- name: test
image: public.ecr.aws/l6m2t8p7/docker-2048:latest
resources:
limits:
cpu: 100m
memory: 250Mi
ports:
- containerPort: 80
name: test
기본적인 워크로드를 배포해보자.
실습을 위해 빨리 끝냈다!!
배포되는데 시간이 제법 걸리긴 하지만, 결국 원하는대로 워크로드가 생기는 것을 확인할 수 있다.
잡 띄우기
apiVersion: batch/v1
kind: Job
metadata:
name: busybox
namespace: fargate
spec:
template:
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh", "-c", "sleep 10"]
restartPolicy: Never
파게이트를 사용하기 가장 적합한 워크로드는 일시적으로만 사용하는 잡일 것이다.
주의점은 잡은 자신이 사라지기 전까지는 파드를 계속 남겨둔다는 점이다.
그래서 위의 파드도 성공으로 종료된 이후 계속 살아있는 것을 볼 수 있다.
이럴 경우 파드가 사라지지 않았기 때문에 파게이트의 노드 역시 계속 남아있게 된다.
그래서 파게이트에서 잡을 사용할 때는 ttlSecondsAfterFinished
을 걸어 잡이 끝난 후 알아서 삭제되도록 하거나, Cronjob이나 KEDA의 ScaledJob과 같은 잡 상위 컨트롤러를 활용하는 것이 좋다.
결론
서버리스 환경을 사용하면 이득을 보는 작업이 있다면, 파게이트는 매우 매력적인 서비스이다.
실무에서 항상 기동되지는 않는 일들은 적당히 파게이트를 활용하여 리소스 절약이 가능할 것이다.
이것 뿐만이 아니더라도 사용 케이스처럼 카펜터를 파게이트에 올린 후 실제 인스턴스 운영 환경을 전적으로 관리하게 하는 전략도 매우 좋다고 생각한다.
이전 글, 다음 글
다른 글 보기
이름 | index | noteType | created |
---|---|---|---|
1W - EKS 설치 및 액세스 엔드포인트 변경 실습 | 1 | published | 2025-02-03 |
2W - 테라폼으로 환경 구성 및 VPC 연결 | 2 | published | 2025-02-11 |
2W - EKS VPC CNI 분석 | 3 | published | 2025-02-11 |
2W - ALB Controller, External DNS | 4 | published | 2025-02-15 |
3W - kubestr과 EBS CSI 드라이버 | 5 | published | 2025-02-21 |
3W - EFS 드라이버, 인스턴스 스토어 활용 | 6 | published | 2025-02-22 |
4W - 번외 AL2023 노드 초기화 커스텀 | 7 | published | 2025-02-25 |
4W - EKS 모니터링과 관측 가능성 | 8 | published | 2025-02-28 |
4W - 프로메테우스 스택을 통한 EKS 모니터링 | 9 | published | 2025-02-28 |
5W - HPA, KEDA를 활용한 파드 오토스케일링 | 10 | published | 2025-03-07 |
5W - Karpenter를 활용한 클러스터 오토스케일링 | 11 | published | 2025-03-07 |
6W - PKI 구조, CSR 리소스를 통한 api 서버 조회 | 12 | published | 2025-03-15 |
6W - api 구조와 보안 1 - 인증 | 13 | published | 2025-03-15 |
6W - api 보안 2 - 인가, 어드미션 제어 | 14 | published | 2025-03-16 |
6W - EKS 파드에서 AWS 리소스 접근 제어 | 15 | published | 2025-03-16 |
6W - EKS api 서버 접근 보안 | 16 | published | 2025-03-16 |
7W - 쿠버네티스의 스케줄링, 커스텀 스케줄러 설정 | 17 | published | 2025-03-22 |
7W - EKS Fargate | 18 | published | 2025-03-22 |
7W - EKS Automode | 19 | published | 2025-03-22 |
8W - 아르고 워크플로우 | 20 | published | 2025-03-30 |
8W - 아르고 롤아웃 | 21 | published | 2025-03-30 |
8W - 아르고 CD | 22 | published | 2025-03-30 |
8W - CICD | 23 | published | 2025-03-30 |
9W - EKS 업그레이드 | 24 | published | 2025-04-02 |
10W - Vault를 활용한 CICD 보안 | 25 | published | 2025-04-16 |
11W - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-04-18 |
11주차 - EKS에서 FSx, Inferentia 활용하기 | 26 | published | 2025-05-11 |
12W - VPC Lattice 기반 gateway api | 27 | published | 2025-04-27 |
관련 문서
이름 | noteType | created |
---|---|---|
Amazon Fargate | knowledge | 2024-11-03 |